import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import Isomap, LocallyLinearEmbedding, SpectralEmbedding, TSNE

# ---------------------- 解决字体显示问题 ----------------------
plt.rcParams['font.sans-serif'] = ['SimSun']  # 指定 SimSun 字体（兼容中文，无中文可换 'Arial'）
plt.rcParams['axes.unicode_minus'] = False  # 修复负号显示异常

# 1. 加载本地 MNIST 数据集
print("正在加载 MNIST 数据集...")
train_path = r"D:\mjh1\digit-recognizer\digit-recognizer\train.csv"
test_path = r"D:\mjh1\digit-recognizer\digit-recognizer\test.csv"

# 验证文件是否存在
if not os.path.exists(train_path):
    raise FileNotFoundError(f"训练集文件不存在: {train_path}")
if not os.path.exists(test_path):
    raise FileNotFoundError(f"测试集文件不存在: {test_path}")

# 加载数据
train_data = pd.read_csv(train_path)
test_data = pd.read_csv(test_path)

# 分离特征和标签
y_train = train_data['label'].values
X_train = train_data.drop('label', axis=1).values
X_test = test_data.values

print(f"训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}")


# 2. 可视化原始图像（优化标题、布局）
def plot_digits(data, title="原始手写数字图像"):
    fig, axes = plt.subplots(4, 10, figsize=(12, 5),  # 增大尺寸，避免挤压
                             subplot_kw={'xticks': [], 'yticks': []},
                             gridspec_kw=dict(hspace=0.3, wspace=0.1))  # 调整子图间距
    for i, ax in enumerate(axes.flat):
        ax.imshow(data[i].reshape(28, 28), cmap='binary', interpolation='nearest')
    plt.suptitle(title, fontsize=14, fontweight='bold')  # 突出标题
    plt.tight_layout()  # 自动优化布局
    plt.show()


plot_digits(X_train)


# 3. 降维与可视化函数（优化标题、标签显示）
def reduce_and_visualize(X, y, reducer, method_name, sample_size=1000):
    """执行降维并可视化结果"""
    # 随机采样（避免计算量过大）
    if len(X) > sample_size:
        idx = np.random.choice(len(X), sample_size, replace=False)
        X_sample, y_sample = X[idx], y[idx]
    else:
        X_sample, y_sample = X, y

    print(f"执行 {method_name} 降维...")
    X_reduced = reducer.fit_transform(X_sample)

    plt.figure(figsize=(9, 7))  # 增大画布，容纳标题、标签
    scatter = plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y_sample,
                          cmap=plt.cm.get_cmap('tab10', 10), s=5, alpha=0.8)
    plt.colorbar(scatter, ticks=range(10), label='数字标签')
    plt.title(f'{method_name} 降维结果', fontsize=14, fontweight='bold')
    plt.xlabel('维度 1', fontsize=12)
    plt.ylabel('维度 2', fontsize=12)
    plt.tight_layout()  # 自动调整布局，避免文字挤压
    plt.show()


# 4. 执行不同降维方法
# PCA (快速)
pca = PCA(n_components=2)
reduce_and_visualize(X_train, y_train, pca, 'PCA', sample_size=5000)

# Isomap (较慢，适合非线性流形)
isomap = Isomap(n_components=2, n_neighbors=15)
reduce_and_visualize(X_train, y_train, isomap, 'Isomap', sample_size=1000)

# LLE (较慢，适合非线性流形)
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=15, method='standard')
reduce_and_visualize(X_train, y_train, lle, '局部线性嵌入 (LLE)', sample_size=1000)

# LE (拉普拉斯特征映射，较慢)
le = SpectralEmbedding(n_components=2, n_neighbors=15)
reduce_and_visualize(X_train, y_train, le, '谱嵌入 (LE)', sample_size=1000)

# t-SNE (最慢，适合可视化)
tsne = TSNE(n_components=2, perplexity=30, random_state=42, init='pca', learning_rate='auto')
reduce_and_visualize(X_train, y_train, tsne, 't-SNE', sample_size=1000)

print("降维实验完成!")